#ifndef STATEMENT_H__
#define STATEMENT_H__

#include <stdlib.h>
#include "basic.h"
#include "declaration.h"
#include "expression.h"
#include "statement-header.h"
#include "type.h"

#if 0
import std.lib;
import basic;
import declaration;
import expression;
import type;
#endif

struct AssignmentStatement;
struct BlockStatement;
struct CallStatement;
struct EnumDeclarationStatement;
enum   FlowStatement;
struct FunctionDeclarationStatement;
struct IfStatement;
struct LabelStatement;
struct PostfixStatement;
struct ReturnStatement;
struct StructDeclarationStatement;
struct UnionDeclarationStatement;
struct VariableDeclarationStatement;
struct WhileStatement;

enum AssignmentStatementTag
{
	SIMPLE_SET,			// =
	MULTIPLICATION_SET,	// *=
	DIVISION_SET,		// /=
	MODULUS_SET,		// %=
	ADDITION_SET,		// +=
	SUBTRACTION_SET,	// -=

	LEFT_SHIFT_SET,		// <<=
	RIGHT_SHIFT_SET,	// >>=
	BITWISE_AND_SET,	// &=
	BITWISE_OR_SET,		// |=
	BITWISE_XOR_SET,	// ^=
};

struct AssignmentStatement
{
	enum AssignmentStatementTag tag;
	struct Expression* left;
	struct Expression* right;
};

struct AssignmentStatement* allocateAssignmentStatement(
	enum AssignmentStatementTag tag,
	struct Expression* left,
	struct Expression* right)
{
	struct AssignmentStatement* r = malloc(sizeof(struct AssignmentStatement));
	r->tag = tag;
	r->left = left;
	r->right = right;
	return r;
}

void freeAssignmentStatement(struct AssignmentStatement* f)
{
	freeExpression(f->left);
	freeExpression(f->right);
	free(f);
}

struct BlockStatement
{
	struct StatementList* list;
};

struct BlockStatement* allocateBlockStatement(struct StatementList* list)
{
	struct BlockStatement* r = malloc(sizeof(struct BlockStatement));
	r->list = list;
	return r;
}

void freeBlockStatement(struct BlockStatement* f)
{
	if(f == NULL) return;
	freeStatementList(f->list);
	free(f);
}

struct EnumDeclarationStatement
{
	struct EnumDeclaration* declaration;
};

struct EnumDeclarationStatement* allocateEnumDeclarationStatement(
	struct EnumDeclaration* declaration)
{
	struct EnumDeclarationStatement* r
		= malloc(sizeof(struct EnumDeclarationStatement));
	r->declaration = declaration;
	return r;
}

void freeEnumDeclarationStatement(struct EnumDeclarationStatement* f)
{
	if(f == NULL) return;
	freeEnumDeclaration(f->declaration);
	free(f);
}

struct ExpressionStatement
{
	struct Expression* expression;
};

struct ExpressionStatement* allocateExpressionStatement(
	struct Expression* expression)
{
	struct ExpressionStatement* r = malloc(sizeof(struct ExpressionStatement));
	r->expression = expression;
	return r;
}

void freeExpressionStatement(struct ExpressionStatement* f)
{
	if(f == NULL) return;
	freeExpression(f->expression);
	free(f);
}

enum FlowStatement
{
	BREAK,
	CONTINUE
};

void freeFlowStatement(enum FlowStatement* f) { free(f); }

struct FunctionDeclarationStatement
{
	struct FunctionDeclaration* declaration;
};

struct FunctionDeclarationStatement* allocateFunctionDeclarationStatement(
	struct FunctionDeclaration* declaration)
{
	struct FunctionDeclarationStatement* r
		= malloc(sizeof(struct FunctionDeclarationStatement));
	r->declaration = declaration;
	return r;
}

void freeFunctionDeclarationStatement(struct FunctionDeclarationStatement* f)
{
	if(f == NULL) return;
	freeFunctionDeclaration(f->declaration);
	free(f);
}

struct IfStatement
{
	struct Expression* condition;
	struct Statement* conditional;
	struct Statement* otherwise;
};

struct IfStatement* allocateIfStatement(
		struct Expression* condition,
		struct Statement* conditional,
		struct Statement* otherwise)
{
	struct IfStatement* r = malloc(sizeof(struct IfStatement));
	r->condition = condition;
	r->conditional = conditional;
	r->otherwise = otherwise;
	return r;
}

void freeIfStatement(struct IfStatement* f)
{
	if(f == NULL) return;
	freeExpression(f->condition);
	freeStatement(f->conditional);
	freeStatement(f->otherwise);
	free(f);
}

struct LabelStatement
{
	struct Name* name;
	struct Statement* statement;
};

struct LabelStatement* allocateLabelStatement(
	struct Name* name,
	struct Statement* statement)
{
	struct LabelStatement* r = malloc(sizeof(struct LabelStatement));
	r->name = name;
	r->statement = statement;
	return r;
}

void freeLabelStatement(struct LabelStatement* f)
{
	if(f == NULL) return;
	freeName(f->name);
	freeStatement(f->statement);
	free(f);
}

enum PostfixStatementTag
{
	INCREMENT,
	DECREMENT
};

struct PostfixStatement
{
	enum PostfixStatementTag tag;
	struct Expression* expression;
};

struct PostfixStatement* allocatePostfixStatement(
	enum PostfixStatementTag tag,
	struct Expression* expression)
{
	struct PostfixStatement* r = malloc(sizeof(struct PostfixStatement));
	r->tag = tag;
	r->expression = expression;
	return r;
}

void freePostfixStatement(struct PostfixStatement* f)
{
	if(f == NULL) return;
	freeExpression(f->expression);
	free(f);
}

struct ReturnStatement
{
	struct Type* type;
	struct Expression* expression;
};

struct ReturnStatement* allocateReturnStatement(
	struct Type* type,
	struct Expression* expression)
{
	struct ReturnStatement* r = malloc(sizeof(struct ReturnStatement));
	r->type = type;
	r->expression = expression;
	return r;
}

void freeReturnStatement(struct ReturnStatement* f)
{
	if(f == NULL) return;
	freeType(f->type);
	freeExpression(f->expression);
	free(f);
}

struct StructDeclarationStatement
{
	struct StructDeclaration* declaration;
};

struct StructDeclarationStatement* allocateStructDeclarationStatement(
	struct StructDeclaration* declaration)
{
	struct StructDeclarationStatement* r
		= malloc(sizeof(struct StructDeclarationStatement));
	r->declaration = declaration;
	return r;
}

void freeStructDeclarationStatement(struct StructDeclarationStatement* f)
{
	if(f == NULL) return;
	freeStructDeclaration(f->declaration);
	free(f);
}

struct UnionDeclarationStatement
{
	struct UnionDeclaration* declaration;
};

struct UnionDeclarationStatement* allocateUnionDeclarationStatement(
	struct UnionDeclaration* declaration)
{
	struct UnionDeclarationStatement* r
		= malloc(sizeof(struct UnionDeclarationStatement));
	r->declaration = declaration;
	return r;
}

void freeUnionDeclarationStatement(struct UnionDeclarationStatement* f)
{
	if(f == NULL) return;
	freeUnionDeclaration(f->declaration);
	free(f);
}

struct VariableDeclarationStatement
{
	struct VariableDeclaration* declaration;
};

struct VariableDeclarationStatement* allocateVariableDeclarationStatement(
	struct VariableDeclaration* declaration)
{
	struct VariableDeclarationStatement* r
		= malloc(sizeof(struct VariableDeclarationStatement));
	r->declaration = declaration;
	return r;
}

void freeVariableDeclarationStatement(struct VariableDeclarationStatement* f)
{
	if(f == NULL) return;
	freeVariableDeclaration(f->declaration);
	free(f);
}

struct WhileStatement
{
	struct Expression* condition;
	struct Statement* statement;
};

struct WhileStatement* allocateWhileStatement(
		struct Expression* condition,
		struct Statement* statement)
{
	struct WhileStatement* r = malloc(sizeof(struct WhileStatement));
	r->condition = condition;
	r->statement = statement;
	return r;
};

void freeWhileStatement(struct WhileStatement* f)
{
	if(f == NULL) return;
	freeExpression(f->condition);
	freeStatement(f->statement);
	free(f);
}

struct Statement* allocateStatement(enum StatementTag tag, void* statement)
{
	struct Statement* r = malloc(sizeof(struct Statement));
	r->tag = tag;
	r->statement = statement;
	return r;
}

void freeStatement(struct Statement* s)
{
	if(s == NULL) return;

	if(s->tag == ASSIGNMENT_STATEMENT)
		freeAssignmentStatement(s->statement);

	else if(s->tag == BLOCK_STATEMENT)
		freeBlockStatement(s->statement);

	else if(s->tag == ENUM_DECLARATION_STATEMENT)
		freeEnumDeclarationStatement(s->statement);

	else if(s->tag == EXPRESSION_STATEMENT)
		freeExpressionStatement(s->statement);

	else if(s->tag == FLOW_STATEMENT)
		freeFlowStatement(s->statement);

	else if(s->tag == FUNCTION_DECLARATION_STATEMENT)
		freeFunctionDeclarationStatement(s->statement);

	else if(s->tag == IF_STATEMENT)
		freeIfStatement(s->statement);

	else if(s->tag == POSTFIX_STATEMENT)
		freePostfixStatement(s->statement);

	else if(s->tag == RETURN_STATEMENT)
		freeReturnStatement(s->statement);

	else if(s->tag == STRUCT_DECLARATION_STATEMENT)
		freeStructDeclarationStatement(s->statement);

	else if(s->tag == UNION_DECLARATION_STATEMENT)
		freeUnionDeclarationStatement(s->statement);

	else if(s->tag == VARIABLE_DECLARATION_STATEMENT)
		freeVariableDeclarationStatement(s->statement);

	else if(s->tag == WHILE_STATEMENT)
		freeWhileStatement(s->statement);

	free(s);
}

#endif
